#pragma once
#include <stdint.h>
#include "base/core/com_define.h"

template<typename TYPE = float>
class Vector2 
{
public:
	TYPE x, y;

public:
	inline Vector2() 
	{
		x = 0.0f;
		y = 0.0f;
	}

	inline Vector2(const TYPE ValueX, const TYPE ValueY)
		:x(ValueX), y(ValueY) {}

	inline explicit Vector2(const TYPE Value)
		:x(Value), y(Value) {}

	inline explicit Vector2(const TYPE inVec[2])
		:x(inVec[0]), y(inVec[1]) {}

	inline explicit Vector2(TYPE* const r)
		:x(r[0]), y(r[1]) {}

	inline TYPE operator[] (const uint32_t i) const 
	{
		MMO_ASSERT(i < 2);
		return *(&x+i);
	}

	inline TYPE& operator[] (const uint32_t i) 
	{
		MMO_ASSERT(i < 2);
		return *(&x+i);
	}

	inline Vector2& operator = (const Vector2& Value) 
	{
		x = Value.x;
		y = Value.y;
		return (*this);
	}

	inline Vector2& operator = (const TYPE Value) 
	{
		x = Value;
		y = Value;
		return (*this);
	}

	inline bool operator == (const Vector2& Value) const 
	{
		return (x == Value.x && y == Value.y);
	}

	inline bool operator != (const Vector2& Value) const 
	{
		return (x != Value.x || y != Value.y);
	}

	inline Vector2 operator + (const Vector2& Value) const 
	{
		return Vector2((x + Value.x), (y + Value.y));
	}

	inline Vector2 operator - (const Vector2& Value) const 
	{
		return Vector2((x - Value.x), (y - Value.y));
	}

	inline Vector2 operator * (const TYPE Value) const 
	{
		return Vector2((x * Value), (y * Value));
	}

	inline Vector2 operator * (const Vector2& Value) const 
	{
		return Vector2((x * Value.x), (y * Value.y));
	}

	inline Vector2 operator / (const TYPE Value) const 
	{
		MMO_ASSERT(Value != 0.0f);
		TYPE fInv = 1.0f / Value;
		return Vector2((x * fInv), (y * fInv));
	}

	inline Vector2 operator / (const Vector2& Value) const 
	{
		MMO_ASSERT(Value.x != 0.0f && Value.y != 0.0f);
		return Vector2((x / Value.x), (y / Value.y));
	}

	inline const Vector2& operator + () const 
	{
		return (*this);
	}

	inline Vector2 operator - () const 
	{
		return Vector2(-x, -y);
	}

	inline friend Vector2 operator * (const TYPE Value, const Vector2& Value) 
	{
		return Vector2((Value * Value.x), (Value * Value.y));
	}

	inline friend Vector2 operator / (const TYPE Value, const Vector2& Value) 
	{
		return Vector2((Value / Value.x), (Value / Value.y));
	}

	inline friend Vector2 operator + (const Vector2& Value, const TYPE Value) 
	{
		return Vector2((Value.x + Value), (Value.y + Value));
	}

	inline friend Vector2 operator + (const TYPE Value, const Vector2& Value) 
	{
		return Vector2((Value + Value.x), (Value + Value.y));
	}

	inline friend Vector2 operator - (const Vector2& Value, const TYPE Value) 
	{
		return Vector2((Value.x - Value), (Value.y - Value));
	}

	inline friend Vector2 operator - (const TYPE Value, const Vector2& Value) 
	{
		return Vector2((Value - Value.x), (Value - Value.y));
	}

	inline Vector2& operator += (const Vector2& Value) 
	{
		x += Value.x;
		y += Value.y;
		return (*this);
	}

	inline Vector2& operator += (const TYPE Value) 
	{
		x += Value;
		y += Value;
		return (*this);
	}

	inline Vector2& operator -= (const Vector2& rkVector) 
	{
		x -= rkVector.x;
		y -= rkVector.y;
		return *this;
	}

	inline Vector2& operator -= (const TYPE Value) 
	{
		x -= Value;
		y -= Value;
		return (*this);
	}

	inline Vector2& operator *= (const TYPE Value) 
	{
		x *= Value;
		y *= Value;
		return (*this);
	}

	inline Vector2& operator *= (const Vector2& Value) 
	{
		x *= Value.x;
		y *= Value.y;
		return (*this);
	}

	inline Vector2& operator /= (const TYPE Value) 
	{
		MMO_ASSERT(Value != 0.0f);
		TYPE TempValue = 1.0f / Value;
		x *= TempValue;
		y *= TempValue;
		return (*this);
	}

	inline Vector2& operator /= (const Vector2& Value) 
	{
		MMO_ASSERT((Value.x != 0) && (Value.y != 0));
		x /= Value.x;
		y /= Value.y;
		return (*this);
	}

	inline TYPE Length() const 
	{
		return sqrt(x * x + y * y);
	}

	inline TYPE SquaredLength() const 
	{
		return (x * x + y * y);
	}

	inline TYPE DotProduct(const Vector2& vec) const 
	{
		return (x * vec.x + y * vec.y);
	}

	inline TYPE Normalise() 
	{
		TYPE fLength = sqrt(x * x + y * y);
		if (fLength > 1e-08) 
		{
			TYPE fInvLength = 1.0f / fLength;
			x *= fInvLength;
			y *= fInvLength;
		}
		return fLength;
	}

	inline Vector2 MidPoint(const Vector2& Value) const 
	{
		return Vector2(((x + Value.x) * 0.5f), ((y + Value.y) * 0.5f));
	}

	inline bool operator < (const Vector2& Value) const
	{
		if (x < Value.x && y < Value.y)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	inline bool operator > (const Vector2& Value) const 
	{
		if (x > Value.x && y > Value.y)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	inline void MakeFloor(const Vector2& Value) 
	{
		if (Value.x < x)
		{
			x = Value.x;
		}
		if (Value.y < y)
		{
			y = Value.y;
		}
	}

	inline void MakeCeil(const Vector2& Value)
	{
		if (Value.x > x)
		{
			x = Value.x;
		}
		if (Value.y > y)
		{
			y = Value.y;
		}
	}

	inline Vector2 Perpendicular(void) const 
	{
		return Vector2 (-y, x);
	}

	inline TYPE CrossProduct(const Vector2& Value) const 
	{
		return (x * Value.y - y * Value.x);
	}

	inline bool IsZeroLength(void) const 
	{
		TYPE len = (x * x) + (y * y);
		return (len < (1e-06 * 1e-06));
	}

	inline Vector2 NormalisedCopy(void) const 
	{
		Vector2 temp = *this;
		temp.Normalise();
		return temp;
	}

	inline Vector2 Reflect(const Vector2& Value) const 
	{
		return Vector2(*this - (2 * this->dotProduct(Value) * Value));
	}
};

